/**
* @class GetIt.GridPrinter
* @author Ed Spencer (edward@domine.co.uk)
* Class providing a common way of printing Ext.Components. Ext.ux.Printer.print delegates the printing to a specialised
* renderer class (each of which subclasses Ext.ux.Printer.BaseRenderer), based on the xtype of the component.
* Each renderer is registered with an xtype, and is used if the component to print has that xtype.
*
* See the files in the renderers directory to customise or to provide your own renderers.
*
* Usage example:
*
* var grid = new Ext.grid.GridPanel({
* colModel: //some column model,
* store : //some store
* });
*
* Ext.ux.Printer.print(grid);
*
*/
Ext.ux.Printer = function() {
  
  return {
    /**
* @property renderers
* @type Object
* An object in the form {xtype: RendererClass} which is manages the renderers registered by xtype
*/
    renderers: {},
    
    /**
* Registers a renderer function to handle components of a given xtype
* @param {String} xtype The component xtype the renderer will handle
* @param {Function} renderer The renderer to invoke for components of this xtype
*/
    registerRenderer: function(xtype, renderer) {
      this.renderers[xtype] = new (renderer)();
    },
    
    /**
* Returns the registered renderer for a given xtype
* @param {String} xtype The component xtype to find a renderer for
* @return {Object/undefined} The renderer instance for this xtype, or null if not found
*/
    getRenderer: function(xtype) {
      return this.renderers[xtype];
    },
    
    /**
* Prints the passed grid. Reflects on the grid's column model to build a table, and fills it using the store
* @param {Ext.Component} component The component to print
*/
    print: function(component) {
    	
      var xtypes = component.getXTypes().split('/');
      
      //iterate backwards over the xtypes of this component, dispatching to the most specific renderer
      for (var i = xtypes.length - 1; i >= 0; i--){
        var xtype = xtypes[i],
            renderer = this.getRenderer(xtype);
        
        if (renderer != undefined) {
          renderer.print(component);
          break;
        }
      }
    }
  };
}();
 
/**
* Override how getXTypes works so that it doesn't require that every single class has
* an xtype registered for it.
*/
Ext.override(Ext.Component, {
  getXTypes : function(){
      var tc = this.constructor;
      if(!tc.xtypes){
          var c = [], sc = this;
          while(sc){ //was: while(sc && sc.constructor.xtype) {
            var xtype = sc.constructor.xtype;
            if (xtype != undefined) c.unshift(xtype);
            
            sc = sc.constructor.superclass;
          }
          tc.xtypeChain = c;
          tc.xtypes = c.join('/');
      }
      return tc.xtypes;
  }
});

/**
* @class Ext.ux.Printer.BaseRenderer
* @extends Object
* @author Ed Spencer
* Abstract base renderer class. Don't use this directly, use a subclass instead
*/
Ext.ux.Printer.BaseRenderer = Ext.extend(Object, {
  /**
* Prints the component
* @param {Ext.Component} component The component to print
*/
  print: function(component) {
    var name = component && component.getXType
             ? String.format("print_{0}_{1}", component.getXType(), component.id)
             : "print";
             
    var win = window.open('', name);
    
    win.document.write(this.generateHTML(component));
    win.document.close();
    
    win.print();
    win.close();
  },
  
  /**
* Generates the HTML Markup which wraps whatever this.generateBody produces
* @param {Ext.Component} component The component to generate HTML for
* @return {String} An HTML fragment to be placed inside the print window
*/
  generateHTML: function(component) {
    return new Ext.XTemplate(
      '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">',
      '<html>',
        '<head>',
          '<meta content="text/html; charset=UTF-8" http-equiv="Content-Type" />',
          '<link href="' + this.stylesheetPath + '" rel="stylesheet" type="text/css" media="screen,print" />',
          '<title>' + this.getTitle(component) + '</title>',
        '</head>',
        '<body>',
          this.generateBody(component),
        '</body>',
      '</html>'
    ).apply(this.prepareData(component));
  },
  
  /**
* Returns the HTML that will be placed into the print window. This should produce HTML to go inside the
* <body> element only, as <head> is generated in the print function
* @param {Ext.Component} component The component to render
* @return {String} The HTML fragment to place inside the print window's <body> element
*/
  generateBody: Ext.emptyFn,
  
  /**
* Prepares data suitable for use in an XTemplate from the component
* @param {Ext.Component} component The component to acquire data from
* @return {Array} An empty array (override this to prepare your own data)
*/
  prepareData: function(component) {
    return component;
  },
  
  /**
* Returns the title to give to the print window
* @param {Ext.Component} component The component to be printed
* @return {String} The window title
*/
  getTitle: function(component) {
    return typeof component.getTitle == 'function' ? component.getTitle() : (component.title || "Printing");
  },
  
  /**
* @property stylesheetPath
* @type String
* The path at which the print stylesheet can be found (defaults to 'stylesheets/print.css')
*/
  stylesheetPath: 'stylesheets/print.css'
});



/**
* @class Ext.ux.Printer.GridPanelRenderer
* @extends Ext.ux.Printer.BaseRenderer
* @author Ed Spencer
* Helper class to easily print the contents of a grid. Will open a new window with a table where the first row
* contains the headings from your column model, and with a row for each item in your grid's store. When formatted
* with appropriate CSS it should look very similar to a default grid. If renderers are specified in your column
* model, they will be used in creating the table. Override headerTpl and bodyTpl to change how the markup is generated
*/
Ext.ux.Printer.GridPanelRenderer = Ext.extend(Ext.ux.Printer.BaseRenderer, {
  
  /**
* Generates the body HTML for the grid
* @param {Ext.grid.GridPanel} grid The grid to print
*/
  generateBody: function(grid) {
    var columns = this.getColumns(grid);
    
    //use the headerTpl and bodyTpl XTemplates to create the main XTemplate below
    var headings = this.headerTpl.apply(columns);
    var body = this.bodyTpl.apply(columns);
    
    return String.format('<table border =1 width = 800 height = 400 cellpadding = 5 cellspacing = 0>{0}<tpl for=".">{1}</tpl></table>', headings, body);
  },
  
  /**
* Prepares data from the grid for use in the XTemplate
* @param {Ext.grid.GridPanel} grid The grid panel
* @return {Array} Data suitable for use in the XTemplate
*/
  prepareData: function(grid) {
    //We generate an XTemplate here by using 2 intermediary XTemplates - one to create the header,
    //the other to create the body (see the escaped {} below)
    var columns = this.getColumns(grid);
  
    //build a useable array of store data for the XTemplate
    var data = [];
    grid.store.data.each(function(item) {
      var convertedData = {};
      
      //apply renderers from column model
      Ext.iterate(item.data, function(key, value) {

        Ext.each(columns, function(column) {       	 
          if (column.dataIndex == key) {
            convertedData[key] = column.renderer ? column.renderer(value, null, item) : value;
            return false;
          }
        }, this);////each
      });////iterate
    
      data.push(convertedData);
      
    });
    
  
    return data;
  },
  
  /**
* Returns the array of columns from a grid
* @param {Ext.grid.GridPanel} grid The grid to get columns from
* @return {Array} The array of grid columns
*/
  getColumns: function(grid) {
    var columns = [];
    
   Ext.each(grid.getColumnModel().config, function(col) {
   if (col.hidden != true) columns.push(col);
   }, this);
  
   return columns;
  },
  
  /**
* @property headerTpl
* @type Ext.XTemplate
* The XTemplate used to create the headings row. By default this just uses <th> elements, override to provide your own
*/
  headerTpl: new Ext.XTemplate(
    '<tr>',
      '<tpl for=".">',
        '<th>{header}</th>',
      '</tpl>', 
    '</tr>'
  ),
 
   /**
* @property bodyTpl
* @type Ext.XTemplate
* The XTemplate used to create each row. This is used inside the 'print' function to build another XTemplate, to which the data
* are then applied (see the escaped dataIndex attribute here - this ends up as "{dataIndex}")
*/
  bodyTpl: new Ext.XTemplate(
    '<tr>',
      '<tpl for=".">',
  //      '<td>\{{dataIndex}\}</td>',
        '<td>\{{dataIndex}\}</td>',
      '</tpl>',
    '</tr>'
  )
});
 
Ext.ux.Printer.registerRenderer('grid', Ext.ux.Printer.GridPanelRenderer);